﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace SPWarmUp
{
    internal class HostsFile
    {
        #region Fields
        private string _filePath;
        private string _originalContents;
        private string _backupFilePath;
        private bool _isBackedUp;
        #endregion

        #region Construction
        private HostsFile()
        {
        }
        #endregion

        #region Properties
        /// <summary>
        /// Indicates whether or not this hosts file has been backed up.
        /// </summary>
        public bool IsBackedUp
        {
            get
            {
                return _isBackedUp;
            }
        }
        #endregion

        #region Static Constructor Helpers
        /// <summary>
        /// Opens the Windows hosts file from the default location in the Windows folder.
        /// </summary>
        /// <returns>A new instance of the <see cref="HostsFile"/> if successful, otherwise null.</returns>
        public static HostsFile Open()
        {
            string system = Environment.GetFolderPath( Environment.SpecialFolder.System );
            string file = Path.Combine( system, @"drivers\etc", "hosts" );
            return OpenHelper( file );
        }

        /// <summary>
        /// Opens the Windows hosts file from a custom path.
        /// </summary>
        /// <param name="path">A custom path to the hosts file (just the directory).</param>
        /// <returns>A new instance of the <see cref="HostsFile"/> if successful, otherwise null.</returns>
        public static HostsFile Open( string path )
        {
            string file = Path.Combine( path, "hosts" );
            return OpenHelper( file );
        }
        #endregion

        #region Public API
        /// <summary>
        /// Restores the hosts file back to its original settings.
        /// </summary>
        /// <returns>True if the restore operation succeeded, otherwise False.</returns>
        public bool Restore()
        {
            try
            {
                FileInfo fileInfo = new FileInfo( _filePath );
                using( FileStream fs = fileInfo.Open( FileMode.Truncate ) )
                {
                    using( TextWriter writer = new StreamWriter( fs ) )
                    {
                        writer.Write( _originalContents );
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Backs up a copy of the hosts file to the current directory.
        /// </summary>
        /// <returns>True if the backup operation succeeded, otherwise False.</returns>
        public bool Backup()
        {
            string filename = DateTime.Now.ToString( "hosts_backup_yyyy_MM_dd_HH_mm_ss" ) + ".txt";
            try
            {
                string filepath = Directory.GetCurrentDirectory();
                _backupFilePath = Path.Combine( filepath, filename );
                using( FileStream fs = File.Create( _backupFilePath ) )
                {
                    using( TextWriter writer = new StreamWriter( fs ) )
                    {
                        writer.Write( _originalContents );
                    }
                }
                _isBackedUp = true;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Updates the hosts file with an entry for a specific web server/URL combination.
        /// </summary>
        /// <param name="wfe">A reference to the web front-end server.</param>
        /// <param name="url">A reference to the URL.</param>
        /// <returns>True if the update succeeded, otherwise False.</returns>
        /// <exception cref="InvalidOperationException">A backup has not been performed or the backup failed.</exception>
        public bool Update( WebFrontEndServer wfe, Uri url )
        {
            if( !_isBackedUp )
            {
                throw new InvalidOperationException( "Cannot Update HostsFile unless backed up first" );
            }
            string entry = wfe.IpAddress + "    " + url.Authority;
            try
            {
                FileInfo fileInfo = new FileInfo( _filePath );
                using( FileStream fs = fileInfo.Open( FileMode.Truncate ) )
                {
                    using( TextWriter writer = new StreamWriter( fs ) )
                    {
                        writer.WriteLine( _originalContents );
                        writer.WriteLine( "# SharePoint Warmup Script -- Automated Entry" );
                        writer.WriteLine( entry );
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Deletes the backup file.
        /// </summary>
        public void DeleteBackup()
        {
            try
            {
                File.Delete( _backupFilePath );
            }
            catch
            {
                // Fail silently
            }
        }
        #endregion

        #region Private Methods
        private static HostsFile OpenHelper( string file )
        {
            try
            {
                using( FileStream fs = File.OpenRead( file ) )
                {
                    using( TextReader tr = new StreamReader( fs ) )
                    {
                        HostsFile hostsFile = new HostsFile();
                        hostsFile._originalContents = tr.ReadToEnd();
                        hostsFile._filePath = file;
                        return hostsFile;
                    }
                }
            }
            catch
            {
                return null;
            }
        }
        #endregion
    }
}